﻿using ExcellentProjectPlus.Dominio.Core;
using ExcellentProjectPlus.Infraestructura.Transversales.Logging;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ExcellentProjectPlus.Infraestructura.Data.Core
{
    public abstract class Repositorio<TEntity>
        : IRepositorio<TEntity> where TEntity : class
    {
        #region Members
        ITraceManager _traceManager = null;
        DbContext _contexto = null;

        public DbContext Contexto
        {
            get { return _contexto; }
            set { _contexto = value; }
        }
        DbSet<TEntity> dbSet = null;
        #endregion

        #region Constructors

        public Repositorio(DbContext contexto, ITraceManager traceManager)
        {
            //check preconditions
            if (contexto == null)
                throw new ArgumentNullException("contexto", "");

            if (traceManager == null)
                throw new ArgumentNullException("traceManager", "");

            //set internal values
            this._contexto = contexto;
            this._traceManager = traceManager;
            this.dbSet = _contexto.Set<TEntity>();


            _traceManager.TraceInfo(
                string.Format(CultureInfo.InvariantCulture,
                             "",
                             typeof(TEntity).Name));
        }
        #endregion

        public virtual void Agregar(TEntity entidad)
        {
            if (entidad == null)
                throw new ArgumentNullException("entidad", "");
            dbSet.Add(entidad);
        }

        public virtual void Agregar(List<TEntity> entidades)
        {
            if (entidades == null)
                throw new ArgumentNullException("entidades", "");
            dbSet.AddRange(entidades);
        }

        public virtual void Modificar(TEntity entidad)
        {
            if (entidad == null)
                throw new ArgumentNullException("entidad", "");

            dbSet.Attach(entidad);
            _contexto.Entry(entidad).State = EntityState.Modified;
        }

        public virtual void Modificar(List<TEntity> items)
        {
            if (items == null)
                throw new ArgumentNullException("items", "");

            foreach (TEntity item in items)
            {
                if (item != null)
                {
                    dbSet.Attach(item);
                    _contexto.Entry(item).State = EntityState.Modified;
                }
            }
        }

        public virtual void Eliminar(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Eliminar(entityToDelete);
        }

        public virtual void Eliminar(object[] ids)
        {
            TEntity entityToDelete = dbSet.Find(ids);
            Eliminar(entityToDelete);
        }

        public void Eliminar(TEntity entidad)
        {
            if (_contexto.Entry(entidad).State == EntityState.Detached)
            {
                dbSet.Attach(entidad);
            }
            dbSet.Remove(entidad);
        }

        public void Eliminar(List<TEntity> entidades)
        {
            if (entidades == null)
                throw new ArgumentNullException("entidades", "");

            foreach (TEntity entidad in entidades)
            {
                if (entidad != null)
                {
                    if (_contexto.Entry(entidad).State == EntityState.Detached)
                    {
                        dbSet.Attach(entidad);
                    }

                }
                dbSet.RemoveRange(entidades);
            }
        }


        public virtual TEntity ObtenerPorId(object id)
        {
            return dbSet.Find(id);
        }

        public virtual TEntity ObtenerPorId(object[] ids)
        {
            return dbSet.Find(ids);
        }

        public virtual IEnumerable<TEntity> Obtener(
           Expression<Func<TEntity, bool>> filter = null,
           Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
           string includeProperties = "", int? pageSize = null, int? page = null)
        {

            IQueryable<TEntity> query = dbSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            if (pageSize != null)
            {
                if (orderBy == null)
                    throw new ArgumentNullException("orderBy");
                query = query.Skip(pageSize.Value * (page.Value)).Take(pageSize.Value);
            }

            return query.ToList();

        }


        IQueryable<TEntity> IRepositorio<TEntity>.ObtenerConsulta(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", int? pageSize = null, int? page = null)
        {
            IQueryable<TEntity> query = dbSet.AsQueryable<TEntity>();

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            if (pageSize != null)
            {
                if (orderBy == null)
                    throw new ArgumentNullException("orderBy");
                query = query.Skip(pageSize.Value * (page.Value)).Take(pageSize.Value);
            }

            return query;
        }
    }
}
